#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""
Auto-SSL v2.0 - 智能SSL证书自动化配置工具
支持宝塔面板和通用Nginx环境的一键SSL证书配置
"""

import os
import sys
import subprocess
import time
import re
import json
import argparse
import configparser
from pathlib import Path
from typing import Dict, Optional, Tuple

class Colors:
    """终端颜色类"""
    RED = '\033[0;31m'
    GREEN = '\033[0;32m'
    YELLOW = '\033[1;33m'
    BLUE = '\033[0;34m'
    PURPLE = '\033[0;35m'
    CYAN = '\033[0;36m'
    WHITE = '\033[1;37m'
    NC = '\033[0m'  # No Color

class Logger:
    """日志类"""
    
    @staticmethod
    def info(message: str):
        print(f"{Colors.BLUE}[INFO]{Colors.NC} {message}")
    
    @staticmethod
    def success(message: str):
        print(f"{Colors.GREEN}[SUCCESS]{Colors.NC} {message}")
    
    @staticmethod
    def warning(message: str):
        print(f"{Colors.YELLOW}[WARNING]{Colors.NC} {message}")
    
    @staticmethod
    def error(message: str):
        print(f"{Colors.RED}[ERROR]{Colors.NC} {message}")
    
    @staticmethod
    def input_prompt(message: str) -> str:
        try:
            # 尝试从终端获取输入，处理管道执行的情况
            import sys
            prompt = f"{Colors.CYAN}[INPUT]{Colors.NC} {message}: "
            
            # 检查是否为交互式终端
            if sys.stdin.isatty():
                # 标准输入是终端，正常读取
                return input(prompt).strip()
            else:
                # 标准输入被重定向，尝试从/dev/tty读取
                try:
                    print(prompt, end='', flush=True)
                    with open('/dev/tty', 'r') as tty:
                        return tty.readline().strip()
                except (OSError, IOError):
                    # 无法访问终端，报错退出
                    print(f"\n{Colors.RED}[ERROR]{Colors.NC} 无法获取用户输入")
                    print(f"{Colors.YELLOW}[INFO]{Colors.NC} 请直接运行脚本而不是通过管道")
                    print(f"{Colors.YELLOW}[INFO]{Colors.NC} 或使用非交互模式：--domain example.com")
                    sys.exit(1)
        except KeyboardInterrupt:
            print(f"\n{Colors.YELLOW}[INFO]{Colors.NC} 用户取消操作，退出程序。")
            sys.exit(0)
        except EOFError:
            print(f"\n{Colors.RED}[ERROR]{Colors.NC} 输入流已结束，无法获取用户输入")
            print(f"{Colors.YELLOW}[INFO]{Colors.NC} 请使用非交互模式：--domain example.com")
            sys.exit(1)

class EnvironmentDetector:
    """环境检测类"""
    
    @staticmethod
    def detect_baota() -> bool:
        """检测是否为宝塔面板环境"""
        baota_paths = [
            "/www/server/panel",
            "/etc/init.d/bt"
        ]
        return all(os.path.exists(path) for path in baota_paths)
    
    @staticmethod
    def detect_os() -> Tuple[str, str]:
        """检测操作系统"""
        try:
            with open('/etc/os-release', 'r') as f:
                lines = f.readlines()
            
            os_info = {}
            for line in lines:
                if '=' in line:
                    key, value = line.strip().split('=', 1)
                    os_info[key] = value.strip('"')
            
            name = os_info.get('NAME', 'Unknown')
            version = os_info.get('VERSION_ID', 'Unknown')
            return name, version
        except:
            return 'Unknown', 'Unknown'
    
    @staticmethod
    def detect_nginx() -> bool:
        """检测Nginx是否安装并可用"""
        # 方法1: 检查nginx命令是否可用
        try:
            subprocess.run(['nginx', '-v'], 
                         capture_output=True, check=True)
            return True
        except:
            pass
        
        # 方法2: 检查nginx服务状态
        try:
            result = subprocess.run(['systemctl', 'is-active', 'nginx'], 
                                  capture_output=True, text=True)
            if result.stdout.strip() == 'active':
                return True
        except:
            pass
        
        # 方法3: 检查nginx进程
        try:
            result = subprocess.run(['pgrep', 'nginx'], 
                                  capture_output=True)
            if result.returncode == 0:
                return True
        except:
            pass
        
        # 方法4: 检查常见nginx路径
        nginx_paths = [
            '/usr/sbin/nginx',
            '/usr/bin/nginx',
            '/sbin/nginx',
            '/usr/local/nginx/sbin/nginx',
            '/usr/local/sbin/nginx'
        ]
        
        for path in nginx_paths:
            if os.path.exists(path):
                return True
        
        return False
    
    @staticmethod
    def detect_certbot() -> bool:
        """检测Certbot是否安装"""
        try:
            subprocess.run(['certbot', '--version'], 
                         capture_output=True, check=True)
            return True
        except:
            return False

class SSLConfig:
    """SSL配置类"""
    
    def __init__(self, mode: str = "auto"):
        self.mode = mode
        self.config = self._load_config()
        self.environment = self._detect_environment()
        
    def _load_config(self) -> Dict:
        """加载配置文件"""
        config_file = "/etc/auto-ssl/config.conf"
        default_config = {
            'baota': {
                'vhost_path': '/www/server/panel/vhost',
                'cert_path': '/www/server/panel/vhost/cert',
                'nginx_path': '/www/server/panel/vhost/nginx',
                'webroot_path': '/www/wwwroot'
            },
            'generic': {
                'nginx_conf_path': '/etc/nginx',
                'sites_available': '/etc/nginx/sites-available',
                'sites_enabled': '/etc/nginx/sites-enabled',
                'cert_path': '/etc/ssl/certs',
                'webroot_path': '/var/www'
            }
        }
        
        if os.path.exists(config_file):
            try:
                config = configparser.ConfigParser()
                config.read(config_file)
                # 转换为字典格式
                result = {}
                for section in config.sections():
                    result[section] = dict(config[section])
                return result
            except:
                Logger.warning("配置文件读取失败，使用默认配置")
        
        return default_config
    
    def _detect_environment(self) -> str:
        """检测环境类型"""
        if self.mode == "baota" or os.getenv('AUTO_SSL_MODE') == "baota":
            return "baota"
        elif self.mode == "generic" or os.getenv('AUTO_SSL_MODE') == "generic":
            return "generic"
        elif EnvironmentDetector.detect_baota():
            Logger.info("自动检测到宝塔面板环境")
            return "baota"
        else:
            Logger.info("使用通用Nginx环境")
            return "generic"

class SSLManager:
    """SSL管理器"""
    
    def __init__(self, config: SSLConfig):
        self.config = config
        self.ssl_config = config.config.get(config.environment, {})
        
    def validate_domain(self, domain: str) -> bool:
        """验证域名格式"""
        pattern = r'^([a-zA-Z0-9]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}$'
        return re.match(pattern, domain) is not None
    
    def run_command(self, command: str, description: str = "", check: bool = True, timeout: int = 60) -> Tuple[bool, str]:
        """执行系统命令"""
        try:
            Logger.info(f"执行命令: {command}")
            result = subprocess.run(
                command, 
                shell=True, 
                capture_output=True, 
                text=True,
                timeout=timeout
            )
            
            if result.returncode != 0 and check:
                Logger.error(f"{description} 失败: {result.stderr}")
                return False, result.stderr
            
            return True, result.stdout
        except subprocess.TimeoutExpired:
            Logger.error(f"{description} 超时({timeout}秒)")
            return False, f"命令执行超时({timeout}秒)"
        except Exception as e:
            if check:
                Logger.error(f"{description} 异常: {str(e)}")
                return False, str(e)
            return True, ""
    
    def check_prerequisites(self) -> bool:
        """检查前置条件"""
        Logger.info("检查系统环境...")
        
        # 检查必要工具
        tools = {
            'certbot': EnvironmentDetector.detect_certbot(),
            'nginx': EnvironmentDetector.detect_nginx()
        }
        
        missing_tools = [tool for tool, installed in tools.items() if not installed]
        
        if missing_tools:
            Logger.error(f"缺少必要工具: {', '.join(missing_tools)}")
            Logger.info("请运行安装脚本: curl -sSL https://gitee.com/tght1211/auto-ssl/raw/master/install.sh | sudo bash")
            return False
        
        Logger.success("系统环境检查通过")
        return True
    
    def find_nginx_config(self, domain: str) -> Optional[str]:
        """查找域名对应的nginx配置文件"""
        if self.config.environment == "baota":
            nginx_dir = self.ssl_config.get('nginx_path', '/www/server/panel/vhost/nginx')
            if os.path.exists(nginx_dir):
                # 搜索包含该域名的配置文件
                try:
                    result = subprocess.run(
                        f"find {nginx_dir} -name '*.conf' | xargs grep -l '{domain}'",
                        shell=True, capture_output=True, text=True
                    )
                    if result.returncode == 0 and result.stdout.strip():
                        config_files = result.stdout.strip().split('\n')
                        Logger.info(f"找到包含域名 {domain} 的配置文件:")
                        for i, config_file in enumerate(config_files, 1):
                            Logger.info(f"  {i}. {config_file}")
                        return config_files[0]  # 返回第一个找到的
                except Exception as e:
                    Logger.warning(f"搜索nginx配置文件时出错: {str(e)}")
        return None
    
    def extract_webroot_from_config(self, config_file: str, domain: str) -> Optional[str]:
        """从nginx配置文件中提取网站根目录"""
        try:
            with open(config_file, 'r') as f:
                content = f.read()
            
            # 查找root指令
            import re
            root_pattern = r'root\s+([^;]+);'
            matches = re.findall(root_pattern, content)
            
            if matches:
                webroot = matches[0].strip()
                Logger.info(f"从配置文件中找到网站根目录: {webroot}")
                return webroot
            
        except Exception as e:
            Logger.warning(f"读取nginx配置文件失败: {str(e)}")
        
        return None
    
    def get_webroot_path(self, domain: str) -> str:
        """智能获取网站根目录路径"""
        if self.config.environment == "baota":
            Logger.info(f"正在智能检测域名 {domain} 的网站配置...")
            
            # 先尝试查找现有的nginx配置
            config_file = self.find_nginx_config(domain)
            if config_file:
                webroot = self.extract_webroot_from_config(config_file, domain)
                if webroot and os.path.exists(webroot):
                    Logger.success(f"检测到有效的网站根目录: {webroot}")
                    return webroot
                elif webroot:
                    Logger.warning(f"配置文件中的网站根目录不存在: {webroot}")
            else:
                Logger.warning(f"未找到域名 {domain} 对应的nginx配置文件")
            
            # 如果找不到配置文件或根目录，使用默认路径
            default_path = f"{self.ssl_config.get('webroot_path', '/www/wwwroot')}/{domain}"
            Logger.info(f"使用默认路径: {default_path}")
            return default_path
        else:
            return f"{self.ssl_config.get('webroot_path', '/var/www')}/{domain}"
    
    def apply_certificate(self, domain: str, email: Optional[str] = None) -> bool:
        """申请SSL证书"""
        Logger.info(f"开始申请 {domain} 的 Let's Encrypt 证书...")
        
        webroot_path = self.get_webroot_path(domain)
        
        # 检查网站根目录
        if not os.path.exists(webroot_path):
            Logger.error(f"网站根目录 {webroot_path} 不存在")
            
            # 检查是否为交互模式
            if sys.stdin.isatty():
                Logger.info("请选择以下选项:")
                Logger.info("1. 手动输入正确的网站根目录路径")
                Logger.info("2. 创建默认目录")
                Logger.info("3. 取消SSL配置")
                
                choice = Logger.input_prompt("请选择 [1/2/3]")
                
                if choice == "1":
                    # 手动输入路径
                    custom_path = Logger.input_prompt("请输入网站根目录的完整路径")
                    if custom_path and os.path.exists(custom_path):
                        webroot_path = custom_path
                        Logger.success(f"使用自定义路径: {webroot_path}")
                    else:
                        Logger.error("输入的路径无效或不存在")
                        return False
                elif choice == "2":
                    # 创建目录
                    try:
                        os.makedirs(webroot_path, mode=0o755, exist_ok=True)
                        Logger.success(f"成功创建目录: {webroot_path}")
                    except Exception as e:
                        Logger.error(f"创建目录失败: {str(e)}")
                        return False
                else:
                    Logger.info("取消SSL配置")
                    return False
            else:
                # 非交互模式，尝试创建默认目录
                Logger.warning("非交互模式，尝试创建默认目录")
                try:
                    os.makedirs(webroot_path, mode=0o755, exist_ok=True)
                    Logger.success(f"成功创建目录: {webroot_path}")
                except Exception as e:
                    Logger.error(f"创建目录失败: {str(e)}")
                    Logger.error("请手动创建网站根目录或使用交互模式")
                    return False
        
        # 确保 .well-known 目录存在
        well_known_path = f"{webroot_path}/.well-known"
        if not os.path.exists(well_known_path):
            try:
                os.makedirs(well_known_path, mode=0o755, exist_ok=True)
            except Exception as e:
                Logger.error(f"创建 {well_known_path} 目录失败: {str(e)}")
                return False
        
        # 申请证书 - 智能邮箱处理
        if email:
            # 使用传入的邮箱参数
            Logger.info(f"使用指定邮箱: {email}")
        else:
            email = f"admin@{domain}"  # 默认邮箱
            
            # 只在交互模式下询问邮箱
            if sys.stdin.isatty():
                try:
                    user_email = Logger.input_prompt(f"请输入邮箱地址 (回车使用默认: {email})")
                    if user_email.strip():
                        email = user_email.strip()
                    Logger.info(f"使用邮箱: {email}")
                except (SystemExit, EOFError):
                    Logger.info(f"使用默认邮箱: {email}")
            else:
                Logger.info(f"非交互模式，使用默认邮箱: {email}")
        
        command = f"certbot certonly --webroot -w {webroot_path} -d {domain} --non-interactive --agree-tos --email {email}"
        success, output = self.run_command(command, "申请 Let's Encrypt 证书")
        
        if success:
            Logger.success(f"成功申请 {domain} 的证书")
            return True
        else:
            return False
    
    def configure_nginx_baota(self, domain: str) -> bool:
        """配置宝塔面板Nginx"""
        Logger.info(f"配置宝塔面板Nginx for {domain}...")
        
        # 创建证书目录
        cert_dir = f"{self.ssl_config.get('cert_path', '/www/server/panel/vhost/cert')}/{domain}"
        if not os.path.exists(cert_dir):
            try:
                os.makedirs(cert_dir, mode=0o755, exist_ok=True)
                Logger.success(f"成功创建证书目录: {cert_dir}")
            except Exception as e:
                Logger.error(f"创建证书目录失败: {str(e)}")
                return False
        
        # 复制证书文件
        source_cert = f"/etc/letsencrypt/live/{domain}/fullchain.pem"
        source_key = f"/etc/letsencrypt/live/{domain}/privkey.pem"
        target_cert = f"{cert_dir}/fullchain.pem"
        target_key = f"{cert_dir}/privkey.pem"
        
        if not os.path.exists(source_cert) or not os.path.exists(source_key):
            Logger.error("证书文件不存在，请先申请证书")
            return False
        
        try:
            self.run_command(f"cp {source_cert} {target_cert}", "复制证书文件")
            self.run_command(f"cp {source_key} {target_key}", "复制私钥文件")
            self.run_command(f"chmod 644 {target_cert}", "设置证书文件权限")
            self.run_command(f"chmod 600 {target_key}", "设置私钥文件权限")
            Logger.success("证书文件复制完成")
        except Exception as e:
            Logger.error(f"复制证书文件失败: {str(e)}")
            return False
        
        # 更新Nginx配置
        return self._update_nginx_config_baota(domain)
    
    def configure_nginx_generic(self, domain: str) -> bool:
        """配置通用Nginx"""
        Logger.info(f"配置通用Nginx for {domain}...")
        
        sites_available = self.ssl_config.get('sites_available', '/etc/nginx/sites-available')
        sites_enabled = self.ssl_config.get('sites_enabled', '/etc/nginx/sites-enabled')
        
        config_file = f"{sites_available}/{domain}"
        
        # 创建Nginx配置
        nginx_config = self._generate_nginx_config_generic(domain)
        
        try:
            with open(config_file, 'w') as f:
                f.write(nginx_config)
            
            # 启用站点
            link_target = f"{sites_enabled}/{domain}"
            if not os.path.exists(link_target):
                os.symlink(config_file, link_target)
            
            Logger.success(f"Nginx配置创建完成: {config_file}")
            return True
        except Exception as e:
            Logger.error(f"创建Nginx配置失败: {str(e)}")
            return False
    
    def _update_nginx_config_baota(self, domain: str) -> bool:
        """更新宝塔面板Nginx配置"""
        # 先尝试找到实际的配置文件
        config_file = self.find_nginx_config(domain)
        
        if not config_file:
            # 如果找不到，尝试使用默认命名
            default_config = f"{self.ssl_config.get('nginx_path', '/www/server/panel/vhost/nginx')}/{domain}.conf"
            if os.path.exists(default_config):
                config_file = default_config
            else:
                Logger.error(f"找不到域名 {domain} 对应的Nginx配置文件")
                Logger.info("请确保已在宝塔面板中创建了该域名的网站")
                return False
        
        Logger.info(f"使用配置文件: {config_file}")
        
        if not os.path.exists(config_file):
            Logger.error(f"Nginx配置文件 {config_file} 不存在")
            return False
        
        try:
            with open(config_file, 'r') as f:
                content = f.read()
            
            # 检查是否已配置SSL
            if 'ssl_certificate' in content:
                Logger.info(f"{domain} 的SSL配置已存在，跳过更新")
                return True
            
            # 添加SSL配置
            ssl_config = f"""
    # SSL配置
    ssl_certificate    /www/server/panel/vhost/cert/{domain}/fullchain.pem;
    ssl_certificate_key    /www/server/panel/vhost/cert/{domain}/privkey.pem;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers EECDH+CHACHA20:EECDH+CHACHA20-draft:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:!MD5;
    ssl_prefer_server_ciphers on;
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 10m;
    add_header Strict-Transport-Security "max-age=31536000";
    error_page 497  https://$host$request_uri;"""
            
            # 插入SSL配置
            insert_marker = "#error_page 404/404.html;"
            if insert_marker in content:
                content = content.replace(insert_marker, insert_marker + ssl_config)
            else:
                # 在server块中查找合适位置插入
                content = re.sub(r'(server\s*\{[^}]*)', r'\1' + ssl_config, content, 1)
            
            # 添加443端口监听
            if 'listen 443 ssl' not in content:
                content = re.sub(r'(listen\s+80[^;]*;)', r'\1\n    listen 443 ssl;', content)
            
            with open(config_file, 'w') as f:
                f.write(content)
            
            Logger.success("Nginx配置更新完成")
            return True
        except Exception as e:
            Logger.error(f"更新Nginx配置失败: {str(e)}")
            return False
    
    def _generate_nginx_config_generic(self, domain: str) -> str:
        """生成通用Nginx配置"""
        webroot = self.get_webroot_path(domain)
        
        return f"""
server {{
    listen 80;
    listen 443 ssl;
    server_name {domain};
    root {webroot};
    index index.html index.htm index.php;

    # SSL配置
    ssl_certificate /etc/letsencrypt/live/{domain}/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/{domain}/privkey.pem;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers EECDH+CHACHA20:EECDH+CHACHA20-draft:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:!MD5;
    ssl_prefer_server_ciphers on;
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 10m;

    # 安全头
    add_header Strict-Transport-Security "max-age=31536000" always;
    add_header X-Frame-Options DENY;
    add_header X-Content-Type-Options nosniff;

    # HTTP重定向到HTTPS
    if ($scheme != "https") {{
        return 301 https://$host$request_uri;
    }}

    location / {{
        try_files $uri $uri/ =404;
    }}

    location ~ /\.well-known {{
        allow all;
    }}
}}
"""
    
    def test_and_reload_nginx(self) -> bool:
        """测试并重载Nginx配置"""
        Logger.info("测试Nginx配置...")
        
        success, output = self.run_command("nginx -t", "测试Nginx配置")
        if not success:
            return False
        
        Logger.success("Nginx配置测试通过")
        Logger.info("重新加载Nginx...")
        
        # 尝试多种重载方式
        reload_commands = [
            "nginx -s reload",
            "systemctl reload nginx",
            "service nginx reload"
        ]
        
        for cmd in reload_commands:
            success, output = self.run_command(cmd, "重新加载Nginx", check=False)
            if success:
                Logger.success("Nginx重新加载成功")
                return True
        
        Logger.error("Nginx重新加载失败")
        return False
    
    def verify_ssl(self, domain: str) -> bool:
        """验证SSL证书"""
        Logger.info(f"验证 {domain} 的SSL证书...")
        
        command = f"echo | openssl s_client -connect {domain}:443 -servername {domain} 2>/dev/null | openssl x509 -noout -dates"
        success, output = self.run_command(command, "验证SSL证书", check=False)
        
        if success and output:
            Logger.success(f"SSL证书验证成功:\n{output}")
            return True
        else:
            Logger.warning("SSL证书验证失败，请检查域名解析和防火墙设置")
            return False
    
    def setup_auto_renewal(self) -> bool:
        """设置SSL证书自动续期"""
        Logger.info("配置SSL证书自动续期...")
        
        # 检查是否已经有续期任务
        result = subprocess.run("crontab -l 2>/dev/null | grep -q 'certbot renew'", 
                               shell=True, capture_output=True, text=True)
        
        if result.returncode == 0:
            Logger.info("自动续期任务已存在，跳过配置")
            return True
        
        # 创建续期脚本
        renewal_script = self._create_renewal_script()
        if not renewal_script:
            Logger.warning("创建续期脚本失败")
            return False
        
        # 设置cron任务
        if self._setup_cron_job(renewal_script):
            Logger.success("自动续期配置完成")
            Logger.info("证书将在到期前自动续期")
            return True
        else:
            Logger.warning("cron任务设置失败，请手动配置")
            return False
    
    def _create_renewal_script(self) -> Optional[str]:
        """创建续期脚本"""
        script_path = "/usr/local/bin/auto-ssl-renew.sh"
        
        try:
            script_content = """#!/bin/bash
# Auto-SSL 证书自动续期脚本
# 生成时间: $(date)

LOG_FILE="/var/log/auto-ssl-renew.log"

log_message() {
    echo "$(date '+%Y-%m-%d %H:%M:%S') $1" >> "$LOG_FILE"
}

log_message "开始检查证书续期..."

# 续期证书
if certbot renew --quiet --no-self-upgrade; then
    log_message "证书检查完成"
    
    # 重载nginx服务
    if command -v systemctl >/dev/null 2>&1; then
        systemctl reload nginx
        log_message "Nginx服务已重载"
    elif command -v service >/dev/null 2>&1; then
        service nginx reload
        log_message "Nginx服务已重载"
    else
        nginx -s reload
        log_message "Nginx配置已重载"
    fi
    
    log_message "证书续期检查成功完成"
else
    log_message "证书续期检查失败"
    exit 1
fi
"""
            
            with open(script_path, 'w') as f:
                f.write(script_content)
            
            # 设置执行权限
            os.chmod(script_path, 0o755)
            
            Logger.success(f"续期脚本创建成功: {script_path}")
            return script_path
            
        except Exception as e:
            Logger.error(f"创建续期脚本失败: {str(e)}")
            return None
    
    def _setup_cron_job(self, script_path: str) -> bool:
        """设置cron定时任务"""
        try:
            # 获取当前crontab
            result = subprocess.run("crontab -l 2>/dev/null", 
                                  shell=True, capture_output=True, text=True)
            
            current_cron = result.stdout if result.returncode == 0 else ""
            
            # 添加续期任务（每天凌晨2点和下午2点检查）
            renewal_cron = f"0 2,14 * * * {script_path} >/dev/null 2>&1"
            
            if renewal_cron not in current_cron:
                new_cron = current_cron + "\n" + renewal_cron + "\n"
                
                # 设置新的crontab
                process = subprocess.Popen("crontab -", shell=True, stdin=subprocess.PIPE, text=True)
                process.communicate(input=new_cron)
                
                if process.returncode == 0:
                    Logger.success("Cron定时任务设置成功")
                    Logger.info("续期检查时间: 每天凌晨2点和下午2点")
                    return True
                else:
                    Logger.error("设置cron任务失败")
                    return False
            else:
                Logger.info("Cron任务已存在")
                return True
                
        except Exception as e:
            Logger.error(f"设置cron任务失败: {str(e)}")
            return False
    
    def manual_renew_certificates(self) -> bool:
        """手动续期所有证书"""
        Logger.info("检查所有Let's Encrypt证书的续期状态...")
        
        try:
            # 先检查是否有证书
            Logger.info("列出现有证书...")
            list_command = "certbot certificates"
            list_success, list_output = self.run_command(list_command, "列出证书", check=False, timeout=30)
            
            if not list_success or "No certificates found" in list_output:
                Logger.warning("未找到任何Let's Encrypt证书")
                Logger.info("如果您有证书但未显示，请检查certbot配置")
                return True
            
            Logger.info("找到以下证书:")
            print(list_output)
            
            # 使用certbot renew命令进行干运行检查
            Logger.info("执行证书续期模拟检查...")
            dry_run_command = "certbot renew --dry-run --non-interactive --agree-tos"
            success, output = self.run_command(dry_run_command, "检查证书续期状态", check=False, timeout=120)
            
            if success:
                Logger.success("证书续期检查通过（模拟运行）")
                
                # 实际执行续期
                Logger.info("执行实际的证书续期...")
                renew_command = "certbot renew --non-interactive --agree-tos"
                success, output = self.run_command(renew_command, "执行证书续期", check=False, timeout=300)
                
                if success:
                    Logger.success("证书续期执行完成")
                    
                    # 重载nginx
                    if self.test_and_reload_nginx():
                        Logger.success("Nginx配置已重载")
                    else:
                        Logger.warning("Nginx重载失败，请手动检查")
                    
                    # 显示续期结果
                    Logger.info("续期操作详细信息:")
                    print(output)
                    return True
                else:
                    Logger.error("证书续期执行失败")
                    Logger.info("错误详情:")
                    print(output)
                    return False
            else:
                if "No renewals were attempted" in output or "not due for renewal" in output:
                    Logger.success("所有证书都在有效期内，无需续期")
                    return True
                else:
                    Logger.error("证书续期检查失败")
                    Logger.info("详细信息:")
                    print(output)
                    
                    # 提供诊断建议
                    Logger.info("📋 可能的解决方案:")
                    Logger.info("1. 检查域名DNS解析是否正确")
                    Logger.info("2. 确保80/443端口可访问")
                    Logger.info("3. 检查网站根目录权限")
                    Logger.info("4. 手动运行: certbot renew --verbose")
                    
                    return False
                
        except Exception as e:
            Logger.error(f"证书续期过程出现异常: {str(e)}")
            return False
    
    def configure_ssl(self, domain: str, email: Optional[str] = None) -> bool:
        """配置SSL证书的主流程"""
        Logger.info(f"开始为域名 {domain} 配置SSL证书 (环境: {self.config.environment})")
        
        # 步骤1: 检查前置条件
        if not self.check_prerequisites():
            return False
        
        # 步骤2: 验证域名格式
        if not self.validate_domain(domain):
            Logger.error("域名格式不正确")
            return False
        
        # 步骤3: 申请证书
        if not self.apply_certificate(domain, email):
            Logger.error("申请SSL证书失败")
            return False
        
        # 步骤4: 配置Nginx
        if self.config.environment == "baota":
            if not self.configure_nginx_baota(domain):
                Logger.error("配置宝塔面板Nginx失败")
                return False
        else:
            if not self.configure_nginx_generic(domain):
                Logger.error("配置通用Nginx失败")
                return False
        
        # 步骤5: 测试并重载Nginx
        if not self.test_and_reload_nginx():
            Logger.error("Nginx配置测试或重载失败")
            return False
        
        # 步骤6: 验证SSL证书
        time.sleep(3)  # 等待Nginx完全加载
        self.verify_ssl(domain)
        
        # 步骤7: 设置自动续期
        self.setup_auto_renewal()
        
        Logger.success(f"成功为域名 {domain} 配置SSL证书！")
        Logger.info(f"您现在可以通过 https://{domain} 访问您的网站")
        Logger.info("SSL证书将自动续期，无需手动干预")
        return True

def print_banner():
    """打印程序横幅"""
    print(f"{Colors.BLUE}")
    print("""
    ╔══════════════════════════════════════════════════════════════╗
    ║                         Auto-SSL                             ║
    ║                   通用SSL证书自动化配置                        ║
    ║               支持宝塔面板和标准Nginx环境                      ║
    ╚══════════════════════════════════════════════════════════════╝
    """)
    print(f"{Colors.NC}")

def main():
    """主函数"""
    parser = argparse.ArgumentParser(
        description='Auto-SSL - 通用SSL证书自动化配置工具',
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog="""
使用示例:
  python3 auto_ssl_universal.py                    # 自动检测环境
  python3 auto_ssl_universal.py --mode baota       # 强制使用宝塔模式
  python3 auto_ssl_universal.py --mode generic     # 强制使用通用模式
  python3 auto_ssl_universal.py --domain example.com --mode baota  # 非交互模式
  python3 auto_ssl_universal.py --renew            # 手动续期所有证书
        """
    )
    
    parser.add_argument('--mode', choices=['auto', 'baota', 'generic'], 
                       default='auto', help='运行模式 (默认: auto)')
    parser.add_argument('--domain', help='域名 (可选，未指定时会提示输入)')
    parser.add_argument('--email', help='邮箱地址 (可选，默认: admin@domain)')
    parser.add_argument('--renew', action='store_true', help='手动续期所有证书')
    parser.add_argument('--version', action='version', version='Auto-SSL v2.0.0')
    
    args = parser.parse_args()
    
    print_banner()
    
    # 检查root权限
    if os.geteuid() != 0:
        Logger.error("请使用root权限运行此脚本")
        Logger.info("请使用: sudo python3 auto_ssl_universal.py")
        sys.exit(1)
    
    # 处理续期命令
    if args.renew:
        Logger.info("开始手动续期所有SSL证书...")
        config = SSLConfig(args.mode)
        ssl_manager = SSLManager(config)
        success = ssl_manager.manual_renew_certificates()
        if success:
            Logger.success("所有证书续期检查完成！")
            sys.exit(0)
        else:
            Logger.error("证书续期过程中出现错误")
            sys.exit(1)
    
    # 初始化配置
    config = SSLConfig(args.mode)
    ssl_manager = SSLManager(config)
    
    # 获取域名
    domain = args.domain
    if not domain:
        # 检查是否为管道执行，如果是则提示使用非交互模式
        if not sys.stdin.isatty():
            Logger.error("检测到管道执行模式，无法获取交互式输入")
            Logger.info("请使用非交互模式：")
            Logger.info(f"  python3 {sys.argv[0]} --domain your-domain.com --mode {config.environment}")
            Logger.info("或者直接运行脚本而不是通过管道")
            sys.exit(1)
        
        domain = Logger.input_prompt("请输入要配置SSL证书的域名")
    
    if not domain:
        Logger.error("域名不能为空")
        sys.exit(1)
    
    # 显示环境信息
    os_name, os_version = EnvironmentDetector.detect_os()
    Logger.info(f"操作系统: {os_name} {os_version}")
    Logger.info(f"运行模式: {config.environment}")
    
    # 执行SSL配置
    try:
        success = ssl_manager.configure_ssl(domain, args.email)
        if success:
            Logger.success("SSL证书配置完成！")
            sys.exit(0)
        else:
            Logger.error("SSL证书配置失败")
            sys.exit(1)
    except KeyboardInterrupt:
        Logger.warning("用户中断操作")
        sys.exit(130)
    except Exception as e:
        Logger.error(f"发生未预期的错误: {str(e)}")
        sys.exit(1)

if __name__ == "__main__":
    main()
